[% setvar title Class Methods Introspection: what methods does this object support? %]
<div id="archive-notice">
    <h3>This file is part of the Perl 6 Archive</h3>
    <p>To see what is currently happening visit <a href="http://www.perl6.org/">http://www.perl6.org/</a></p>
</div>
<div class='pod'>
<a name='TITLE'></a><h1>TITLE</h1>
<p>Class Methods Introspection: what methods does this object support?</p>
<a name='VERSION'></a><h1>VERSION</h1>
<pre>  Maintainer: Mark Summerfield &lt;<a href='mailto:summer@perlpress.com'>summer@perlpress.com</a>&gt;
  Date: 28 Sep 2000
  Last Modified: 29 Sep 2000
  Mailing List: <a href='mailto:perl6-language-objects@perl.org'>perl6-language-objects@perl.org</a>
  Number: 335
  Version: 2
  Status: Frozen</pre>
<a name='NOTES ON FREEZE'></a><h1>NOTES ON FREEZE</h1>
<p>In view of the lateness of my submission I've only had one response, from
Piers Cawley; we've exchanged several emails and Piers' suggestions and code
has been incorporated.</p>
<p>There are no issues to resolve.</p>
<p>This RFC does not depend on any other RFC.</p>
<a name='ABSTRACT'></a><h1>ABSTRACT</h1>
<p>This RFC proposes a new UNIVERSAL method which would be used thus:</p>
<pre>    my @method_names = $object-&gt;methods(); # OR MyClass-&gt;methods();</pre>
<p>The <code>methods</code> call would return the public methods of the <code>$object</code> or
class; calling with the argument 'all' would return all the public methods
that the <code>$object</code> or class could respond to.</p>
<a name='DESCRIPTION'></a><h1>DESCRIPTION</h1>
<p>How can we tell which methods an object supports? If we know the names
of the methods we're interested in we can use
<code>$object-&gt;can('methodname')</code>. However if we don't know the possible names
then we're stuck. This RFC proposes a new UNIVERSAL method which would be used
thus:</p>
<pre>    @method_names = $object-&gt;methods(); # OR MyClass-&gt;methods();

    @method_names = $object-&gt;methods('all'); # OR MyClass-&gt;methods('all');</pre>
<p>The first example returns the public methods of the <code>$object</code> or class
itself; the second example returns any public methods that the <code>$object</code> or
class could respond to, i.e. all the methods of the class plus those of any
classes it inherits from.</p>
<p>The <code>methods()</code> method may <i>not</i> be able to determine definitively all the
methods that are available: firstly it is perfectly possible to define new
methods at runtime using <code>eval</code>; secondly if the class has an <code>AUTOLOAD</code>
method then the methods it supports may not be determinable. In the case of
classes that use <code>AUTOLOAD</code> the string 'AUTOLOAD' would naturally be one of
the method names returned so at least the programmer would be aware that any
number of other methods may exist. Thus <code>methods</code> would return the list of
methods known to be available to the object at the time that <code>methods</code> is
called.</p>
<p>Piers Cawley suggested this:
&quot;I would say that in an OO context there's a case for walking the @ISA tree to
find out all the methods that the class will respond to, say
<code>$object-</code>all_methods&gt;, after all, in a complex class hierarchy there may be
classes that implement only one or two methods that are distinct from the
methods of their parent class.&quot;</p>
<p>I think that Piers extension is a valuable one, but propose that it should be
achieved by having an optional argument, 'all' to <code>methods</code> rather than a
separate new method; this ensures the minimum addition to UNIVERSAL and also
leaves open the prospect of additional or different parameters being added in
future.</p>
<p><code>methods</code> could of course be over-ridden on a class-by-class basis; for
example, if your class supported fifty methods, but half of them were so
rarely needed that they were created on-the-fly in AUTOLOAD you could
return all their names if you override <code>methods</code> and return the list of
names yourself. Furthermore as a method of UNIVERSAL <code>methods</code> would not
collide with any existing or subsequent function of that name.</p>
<p>In terms of RFC 188 I would expect <code>methods</code> to return public methods
only.</p>
<p>Why not simply use <code>isa</code> to determine the class? Often knowing the
class is all that is necessary; however sometimes it is useful to know
what the possible methods are. Imagine we have a simulation system with
a core application that sends and receives signals to the objects it is
simulating (by calling their methods). Users may implement the objects
they want simulated by creating their own classes for each type of object.
One approach to this is to supply the user with a base class from which
they inherit. This has a disadvantage -- a given method is always called
in response to a given event because each event in the simulator maps to a
method in the base class. Sometimes we want to &quot;wire up&quot; the simulated
object so that on a particular run event A calls method A, but on another
run event A calls method B. Hardwiring this isn't a problem, but what if
we want to provide the user with this choice interactively -- well, if we
don't know what methods the class supports in the first place we cannot
give the user a list to choose from. The solution is to be able to
introspect on the class they've chosen and show them the list of methods
available; they can then choose which to wire up to which events in the
simulator. I am sure that there are other situations where object
introspection would be useful, for example a class browser.</p>
<a name='IMPLEMENTATION'></a><h1>IMPLEMENTATION</h1>
<p>I think this needs to be implemented in Perl itself so that it can avoid
private methods and can be implemented efficiently, but here's some code which
provides a combined implementation and example:</p>
<pre>    #!/usr/bin/perl -w
    # filename: test.pl
    #
    # Unlike a built-in this method cannot distinguish between `real' 
    # methods and `constants' defined with use constant (but hopefully 
    # that pragma will go away). 
    # We skip private methods by ignoring names that begin with an 
    # underscore; we skip overloaded methods by ignoring names that 
    # begin with an open parenthesis.

    use strict;

    package BaseClass;
    sub baseclass {}

    package DerivedClass1;
    use base 'BaseClass';
    sub derived {}
    sub one {}

    package DerivedClass2;
    use base 'BaseClass';
    sub derived {}
    sub two {}

    package BabyClass;
    use base qw(DerivedClass1 DerivedClass2);
    sub three {}

    package main;
    print &quot;BaseClass:           &quot;, 
        join(&quot;, &quot;, sort BaseClass-&gt;methods()), &quot;\n&quot;;
    print &quot;BaseClass (all):     &quot;, 
        join(&quot;, &quot;, sort BaseClass-&gt;methods('all')), &quot;\n&quot;;
    print &quot;DerivedClass1:       &quot;, 
        join(&quot;, &quot;, sort DerivedClass1-&gt;methods()), &quot;\n&quot;;
    print &quot;DerivedClass1 (all): &quot;, 
        join(&quot;, &quot;, sort DerivedClass1-&gt;methods('all')), &quot;\n&quot;;
    print &quot;DerivedClass2:       &quot;, 
        join(&quot;, &quot;, sort DerivedClass2-&gt;methods()), &quot;\n&quot;;
    print &quot;DerivedClass2 (all): &quot;, 
        join(&quot;, &quot;, sort DerivedClass2-&gt;methods('all')), &quot;\n&quot;;
    print &quot;BabyClass:           &quot;, 
        join(&quot;, &quot;, sort BabyClass-&gt;methods()), &quot;\n&quot;;
    print &quot;BabyClass (all):     &quot;, 
        join(&quot;, &quot;, sort BabyClass-&gt;methods('all')), &quot;\n&quot;;


    package UNIVERSAL;

    use strict;

    sub methods {
        my ($class, $types) = @_;
        $class = ref $class || $class;
        $types ||= '';
        my %classes_seen;
        my %methods;
        my @class = ($class);

        no strict 'refs';
        while ($class = shift @class) {
            next if $classes_seen{$class}++;
            unshift @class, @{&quot;${class}::ISA&quot;} if $types eq 'all';
            # Based on methods_via() in perl5db.pl
            for my $method (grep {not /^[(_]/ and 
                                  defined &amp;{${&quot;${class}::&quot;}{$_}}} 
                            keys %{&quot;${class}::&quot;}) {
                $methods{$method} = wantarray ? undef : $class-&gt;can($method); 
            }
        }
      
        wantarray ? keys %methods : \%methods;
    }

    1;
     </pre>
<p>Here's the output:</p>
<pre>    # ./test.pl 
    BaseClass:           baseclass
    BaseClass (all):     baseclass
    DerivedClass1:       derived, one
    DerivedClass1 (all): baseclass, derived, one
    DerivedClass2:       derived, two
    DerivedClass2 (all): baseclass, derived, two
    BabyClass:           three
    BabyClass (all):     baseclass, derived, one, three, two</pre>
<p>You can test it on any class you like:</p>
<pre>    # perl -de0
    main::(-e:1):   0
      DB&lt;1&gt; require 'test.pl'
    [snipped the tests which get executed]
      DB&lt;2&gt; use CPAN;
      DB&lt;3&gt; p join &quot;, &quot;, sort CPAN-&gt;methods()
    AUTOLOAD, DESTROY, all, all_objects, checklock, cleanup, cwd, 
    delete, exists, fill, find, finddepth, getcwd, has_inst, instance, 
    new, shell, wrap
      DB&lt;4&gt; p join &quot;, &quot;, sort CPAN-&gt;methods('all')
    AUTOLOAD, DESTROY, all, all_objects, checklock, cleanup, cwd, 
    debug, delete, exists, export, export_fail, export_ok_tags, 
    export_tags, export_to_level, fill, find, finddepth, getcwd, 
    has_inst, import, instance, new, require_version, shell, wrap</pre>
<p>Piers suggested that the <code>methods</code> method should return an array of method
names in a list context or a reference to a hash of
<code>method_name =&gt; method_coderef</code> pairs in a scalar context; this has been
incorporated. Piers also rewrote the traversal so that it is done in the
correct order, i.e. the same way that Perl itself searches for methods.</p>
<a name='REFERENCES'></a><h1>REFERENCES</h1>
<p>RFC 188: Objects : Private keys and methods</p>
<p>RFC 193: Objects : Core support for method delegation</p>
<p>RFC 265: Interface polymorphism considered lovely</p>
<p>perl5db.pl</p>
<a name='ACKNOWLEDGEMENTS'></a><h1>ACKNOWLEDGEMENTS</h1>
<p>Piers Cawley for reworking the example implementation and explaining why it
needed reworking!</p>
<p><i>The Librarians</i> for doing great work!</p>
</div>
